package com.wlyuan.index.domain.index.factory;

import com.alibaba.fastjson.JSON;
import com.wlyuan.core.domain.valueobj.OrderId;
import com.wlyuan.core.domain.valueobj.WaybillId;
import com.wlyuan.index.domain.index.OrderIndex;
import com.wlyuan.index.domain.index.builder.OrderIndexBuilder;
import com.wlyuan.index.domain.index.repository.OrderIndexRepository;
import com.wlyuan.index.domain.source.factory.SourceFactory;
import com.wlyuan.index.domain.source.repository.SourceRepository;
import com.wlyuan.index.domain.source.valueobj.SourceId;
import com.wlyuan.lock.annotation.CacheLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class OrderIndexFactory {
    private final SourceRepository sourceRepository;
    private final OrderIndexRepository indexRepository;
    private final SourceFactory sourceFactory;
    private static final String LOCK_NAME = "PC::LOCK::INDEX::ORDER_NEW";

    @CacheLock(cacheName = LOCK_NAME, key = "#orderId.id", message = "订单索引锁定失败")
    public OrderIndex create(OrderId orderId) {
        var source = sourceRepository.ofId(new SourceId(orderId));
        if (null == source) {
            source = sourceFactory.create(orderId);
            logger.info("重建订单索引数据: {}", source);
        }
        var builder = new OrderIndexBuilder(source.decode());
        var index = builder.build();
        indexRepository.save(index);

        if (logger.isDebugEnabled()) {
            logger.debug("订单索引更新: {} {}", orderId, JSON.toJSONString(index));
        }
        return index;
    }

    // FIXME: 批量创建索引没有加锁，存在并发冲突问题
    public List<OrderIndex> create(Collection<OrderId> orderIds) {
        if (CollectionUtils.isEmpty(orderIds)) {
            return Collections.emptyList();
        }

        var sourceIds = orderIds.stream()
                .map(SourceId::new)
                .collect(Collectors.toList());

        var sources = sourceRepository.list(sourceIds);

        var indexes = sources.stream()
                .map(source -> new OrderIndexBuilder(source.decode()))
                .map(OrderIndexBuilder::build)
                .collect(Collectors.toList());
        indexRepository.saveAll(indexes);
        return indexes;
    }

    public void remove(WaybillId waybillId) {
        indexRepository.deleteById(waybillId.getId());
    }
}
